home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 25 / Cream of the Crop 25.iso / os2 / gnuwget.zip / wget-1.4.3 / src / netrc.c < prev    next >
C/C++ Source or Header  |  1997-02-07  |  10KB  |  459 lines

  1. /* Read and parse the .netrc file to get hosts, accounts, and passwords.
  2.    Copyright (C) 1996, Free Software Foundation, Inc.
  3.    Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
  4.    Hrvoje Niksic <hniksic@srce.hr>, 1996
  5.  
  6.    This program is free software; you can redistribute it and/or modify
  7.    it under the terms of the GNU General Public License as published by
  8.    the Free Software Foundation; either version 2 of the License, or
  9.    (at your option) any later version.
  10.  
  11.    This program is distributed in the hope that it will be useful,
  12.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.    GNU General Public License for more details.
  15.  
  16.    You should have received a copy of the GNU General Public License
  17.    along with this program; if not, write to the Free Software
  18.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. /* Compile with -DSTANDALONE to test this module. */
  21.  
  22. #ifdef HAVE_CONFIG_H
  23. #  include <config.h>
  24. #endif /* HAVE_CONFIG_H */
  25.  
  26. #include <stdio.h>
  27. #include <ctype.h>
  28. #include <stdlib.h>
  29. #ifdef HAVE_STRING_H
  30. #  include <string.h>
  31. #else
  32. #  include <strings.h>
  33. #endif
  34. #include <sys/types.h>
  35.  
  36. #include "wget.h"
  37. #include "utils.h"
  38. #include "netrc.h"
  39.  
  40. extern int errno;
  41.  
  42. acc_t *netrc_list;
  43.  
  44. /* Return the correct user and password, given the host, user (as
  45.    given in the URL), and password (as given in the URL).  May return
  46.    NULL.
  47.  
  48.    If SLACK_DEFAULT is set, allow looking for a "default" account.
  49.    You will typically turn it off for HTTP.  */
  50. void
  51. search_netrc(const char *host, const char **acc, const char **passwd,
  52.          int slack_default)
  53. {
  54.    acc_t *l;
  55.    
  56.    /* Acc and password found; all OK. */
  57.    if (*acc && *passwd)
  58.       return;
  59.    if (!*acc && !slack_default)
  60.      return;
  61.    /* Some data not given -- try finding the host. */
  62.    for (l = netrc_list; l; l = l->next)
  63.    {
  64.       if (!l->host)
  65.      continue;
  66.       else if (!strcasecmp(l->host, host))
  67.      break;
  68.    }
  69.    if (l)
  70.    {
  71.       if (*acc)
  72.       {
  73.      /* Looking for password in .netrc. */
  74.      if (!strcmp(l->acc, *acc))
  75.         *passwd = l->passwd; /* Usernames match; password OK. */
  76.      else
  77.         *passwd = NULL;     /* Usernames don't match. */
  78.       }
  79.       else                      /* NOT *acc */
  80.       {
  81.      /* If password was given, use it.  The account is l->acc. */
  82.      *acc = l->acc;
  83.      if (l->passwd)
  84.         *passwd = l->passwd;
  85.       }
  86.       return;
  87.    }
  88.    else
  89.    {
  90.       if (!slack_default)
  91.      return;
  92.       if (*acc)
  93.      return;
  94.       /* Try looking for the default account. */
  95.       for (l = netrc_list; l; l = l->next)
  96.      if (!l->host)
  97.         break;
  98.       if (!l)
  99.      return;
  100.       *acc = l->acc;
  101.       if (!*passwd)
  102.      *passwd = l->passwd;
  103.       return;
  104.    }
  105. }
  106.  
  107.  
  108. #ifdef STANDALONE
  109. /* Normally, these functions would be defined by your package. */
  110. # define xmalloc malloc
  111. # define xstrdup strdup
  112.  
  113. /* The function reads a whole line. It reads the line realloc-ing the
  114.    storage exponentially, doubling the storage after each overflow to
  115.    minimize the number of calls to realloc().
  116.  
  117.    It is not an exemplary of correctness, since it kills off the
  118.    newline (and no, there is no way to know if there was a newline at
  119.    EOF). */
  120. # define xrealloc realloc
  121. # define DYNAMIC_LINE_BUFFER 40
  122.  
  123. char *
  124. read_whole_line (FILE *fp)
  125. {
  126.    char *line;
  127.    int i, bufsize, c;
  128.  
  129.    i = 0;
  130.    bufsize = DYNAMIC_LINE_BUFFER;
  131.    line = xmalloc(bufsize);
  132.    /* Construct the line. */
  133.    while ((c = getc(fp)) != EOF && c != '\n')
  134.    {
  135.       if (i > bufsize - 1)
  136.      line = (char *)xrealloc(line, (bufsize <<= 1));
  137.       line[i++] = c;
  138.    }
  139.    if (c == EOF && !i)
  140.    {
  141.       free(line);
  142.       return NULL;
  143.    }
  144.  
  145.    /* Check for overflow at zero-termination (no need to double the
  146.       buffer in this case. */
  147.    if (i == bufsize)
  148.       line = (char *)xrealloc(line, i + 1);
  149.    line[i] = '\0';
  150.    return line;
  151. }
  152.  
  153. #endif /* STANDALONE */
  154.  
  155. /* Maybe add NEWENTRY to the account information list, LIST.  NEWENTRY is
  156.    set to a ready-to-use acc_t, in any event. */
  157. static void
  158. maybe_add_to_list (acc_t **newentry, acc_t **list)
  159. {
  160.   acc_t *a, *l;
  161.   a = *newentry;
  162.   l = *list;
  163.  
  164.   /* We need an account name in order to add the entry to the list. */
  165.   if (a && ! a->acc)
  166.     {
  167.       /* Free any allocated space. */
  168.       free (a->host);
  169.       free (a->acc);
  170.       free (a->passwd);
  171.     }
  172.   else
  173.     {
  174.       if (a)
  175.     {
  176.       /* Add the current machine into our list. */
  177.       a->next = l;
  178.       l = a;
  179.     }
  180.  
  181.       /* Allocate a new acc_t structure. */
  182.       a = nmalloc (sizeof (acc_t));
  183.     }
  184.  
  185.   /* Zero the structure, so that it is ready to use. */
  186.   memset (a, 0, sizeof(*a));
  187.  
  188.   /* Return the new pointers. */
  189.   *newentry = a;
  190.   *list = l;
  191.   return;
  192. }
  193.  
  194.  
  195. /* Parse a .netrc file (as described in the ftp(1) manual page). */
  196. acc_t *
  197. parse_netrc (const char *path)
  198. {
  199.   FILE *fp;
  200.   char *line, *p, *tok, *premature_token;
  201.   acc_t *current, *retval;
  202.   int ln;
  203.  
  204.   /* The latest token we've seen in the file. */
  205.   enum
  206.   {
  207.     tok_nothing, tok_account, tok_login, tok_macdef, tok_machine, tok_password
  208.   } last_token = tok_nothing;
  209.  
  210.   current = retval = NULL;
  211.  
  212.   fp = fopen (path, "r");
  213.   if (!fp)
  214.     {
  215.       fprintf (stderr, "Cannot read %s (%s).\n", path, mystrerror (errno));
  216.       return retval;
  217.     }
  218.  
  219.   /* Initialize the file data. */
  220.   ln = 0;
  221.   premature_token = NULL;
  222.  
  223.   /* While there are lines in the file... */
  224.   while ((line = read_whole_line (fp)))
  225.     {
  226.       ln ++;
  227.  
  228.       /* Parse the line. */
  229.       p = line;
  230.  
  231.       /* If the line is empty, then end any macro definition. */
  232.       if (last_token == tok_macdef && !*p)
  233.     /* End of macro if the line is empty. */
  234.     last_token = tok_nothing;
  235.  
  236.       /* If we are defining macros, then skip parsing the line. */
  237.       while (*p && last_token != tok_macdef)
  238.     {
  239.       /* Skip any whitespace. */
  240.       while (*p && isspace (*p))
  241.         p ++;
  242.  
  243.       /* Discard end-of-line comments. */
  244.       if (*p == '#')
  245.         break;
  246.  
  247.       tok = p;
  248.  
  249.       /* Find the end of the token. */
  250.       while (*p && !isspace (*p))
  251.         p ++;
  252.  
  253.       /* Null-terminate the token, if it isn't already. */
  254.       if (*p)
  255.         *p ++ = '\0';
  256.  
  257.       switch (last_token)
  258.         {
  259.         case tok_login:
  260.           if (current)
  261.         current->acc = nstrdup (tok);
  262.           else
  263.         premature_token = "login";
  264.           break;
  265.  
  266.         case tok_machine:
  267.           /* Start a new machine entry. */
  268.           maybe_add_to_list (¤t, &retval);
  269.           current->host = nstrdup (tok);
  270.           break;
  271.  
  272.         case tok_password:
  273.           if (current)
  274.         current->passwd = nstrdup (tok);
  275.           else
  276.         premature_token = "password";
  277.           break;
  278.  
  279.           /* We handle most of tok_macdef above. */
  280.         case tok_macdef:
  281.           if (!current)
  282.         premature_token = "macdef";
  283.           break;
  284.  
  285.           /* We don't handle the account keyword at all. */
  286.         case tok_account:
  287.           if (!current)
  288.         premature_token = "account";
  289.           break;
  290.  
  291.           /* We handle tok_nothing below this switch. */
  292.         case tok_nothing:
  293.           break;
  294.         }
  295.  
  296.       if (premature_token)
  297.         {
  298.           fprintf (stderr, "%s:%d: warning: \"%s\" token appears before any machine name\n",
  299.                path, ln, premature_token);
  300.           premature_token = NULL;
  301.         }
  302.  
  303.       if (last_token != tok_nothing)
  304.         /* We got a value, so reset the token state. */
  305.         last_token = tok_nothing;
  306.       else
  307.         {
  308.           /* Fetch the next token. */
  309.           if (!strcmp (tok, "account"))
  310.         last_token = tok_account;
  311.  
  312.           if (!strcmp (tok, "default"))
  313.         {
  314.           maybe_add_to_list (¤t, &retval);
  315.         }
  316.           else if (!strcmp (tok, "login"))
  317.         last_token = tok_login;
  318.  
  319.           else if (!strcmp (tok, "macdef"))
  320.         last_token = tok_macdef;
  321.  
  322.           else if (!strcmp (tok, "machine"))
  323.         last_token = tok_machine;
  324.  
  325.           else if (!strcmp (tok, "password"))
  326.         last_token = tok_password;
  327.  
  328.           else
  329.         fprintf (stderr, "%s:%d: unknown token \"%s\"\n",
  330.              path, ln, tok);
  331.         }
  332.     }
  333.  
  334.       free (line);
  335.     }
  336.  
  337.   fclose (fp);
  338.  
  339.   /* Finalize the last machine entry we found. */
  340.   maybe_add_to_list (¤t, &retval);
  341.   free (current);
  342.  
  343.   /* Reverse the order of the list so that it appears in file order. */
  344.   current = retval;
  345.   retval = NULL;
  346.   while (current)
  347.     {
  348.       acc_t *saved_reference;
  349.  
  350.       /* Change the direction of the pointers. */
  351.       saved_reference = current->next;
  352.       current->next = retval;
  353.  
  354.       /* Advance to the next node. */
  355.       retval = current;
  356.       current = saved_reference;
  357.     }
  358.  
  359.   return retval;
  360. }
  361.  
  362.  
  363. /* Free a netrc list. */
  364. void
  365. free_netrc(acc_t *l)
  366. {
  367.    acc_t *t;
  368.  
  369.    while (l)
  370.    {
  371.       t = l->next;
  372.       if (l->acc)
  373.      free(l->acc);
  374.       if (l->passwd)
  375.      free(l->passwd);
  376.       if (l->host)
  377.      free(l->host);
  378.       free(l);
  379.       l = t;
  380.    }
  381. }
  382.  
  383. #ifdef STANDALONE
  384. #include <sys/types.h>
  385. #include <sys/stat.h>
  386.  
  387. int
  388. main (int argc, char **argv)
  389. {
  390.   struct stat sb;
  391.   char *program_name, *file, *target;
  392.   acc_t *head, *a;
  393.  
  394.   if (argc < 2 || argc > 3)
  395.     {
  396.       fprintf (stderr, "Usage: %s NETRC [HOSTNAME]\n", argv[0]);
  397.       exit (1);
  398.     }
  399.  
  400.   program_name = argv[0];
  401.   file = argv[1];
  402.   target = argv[2];
  403.  
  404.   if (stat (file, &sb))
  405.     {
  406.       fprintf (stderr, "%s: cannot stat %s: %s\n", argv[0], file,
  407.            mystrerror (errno));
  408.       exit (1);
  409.     }
  410.  
  411.   head = parse_netrc (file);
  412.   a = head;
  413.   while (a)
  414.     {
  415.       /* Skip if we have a target and this isn't it. */
  416.       if (target && a->host && strcmp (target, a->host))
  417.     {
  418.       a = a->next;
  419.       continue;
  420.     }
  421.  
  422.       if (!target)
  423.     {
  424.       /* Print the host name if we have no target. */
  425.       if (a->host)
  426.         fputs (a->host, stdout);
  427.       else
  428.         fputs ("DEFAULT", stdout);
  429.  
  430.       fputc (' ', stdout);
  431.     }
  432.  
  433.       /* Print the account name. */
  434.       fputs (a->acc, stdout);
  435.  
  436.       if (a->passwd)
  437.     {
  438.       /* Print the password, if there is any. */
  439.       fputc (' ', stdout);
  440.       fputs (a->passwd, stdout);
  441.     }
  442.  
  443.       fputc ('\n', stdout);
  444.  
  445.       /* Exit if we found the target. */
  446.       if (target)
  447.     exit (0);
  448.       a = a->next;
  449.     }
  450.  
  451.   /* Exit with failure if we had a target, success otherwise. */
  452.   if (target)
  453.     exit (1);
  454.  
  455.   exit (0);
  456. }
  457. #endif /* STANDALONE */
  458.  
  459.